1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.solr.request;
19
20 import org.apache.commons.lang.StringUtils;
21 import org.apache.lucene.index.Fields;
22 import org.apache.lucene.index.LeafReader;
23 import org.apache.lucene.index.LeafReaderContext;
24 import org.apache.lucene.index.MultiPostingsEnum;
25 import org.apache.lucene.index.PostingsEnum;
26 import org.apache.lucene.index.Term;
27 import org.apache.lucene.index.Terms;
28 import org.apache.lucene.index.TermsEnum;
29 import org.apache.lucene.search.BooleanClause.Occur;
30 import org.apache.lucene.search.BooleanQuery;
31 import org.apache.lucene.search.DocIdSetIterator;
32 import org.apache.lucene.search.Filter;
33 import org.apache.lucene.search.FilterCollector;
34 import org.apache.lucene.search.FilteredQuery;
35 import org.apache.lucene.search.LeafCollector;
36 import org.apache.lucene.search.MatchAllDocsQuery;
37 import org.apache.lucene.search.Query;
38 import org.apache.lucene.search.TermQuery;
39 import org.apache.lucene.search.grouping.AbstractAllGroupHeadsCollector;
40 import org.apache.lucene.search.grouping.term.TermAllGroupsCollector;
41 import org.apache.lucene.search.grouping.term.TermGroupFacetCollector;
42 import org.apache.lucene.util.BytesRef;
43 import org.apache.lucene.util.CharsRefBuilder;
44 import org.apache.lucene.util.StringHelper;
45 import org.apache.solr.common.SolrException;
46 import org.apache.solr.common.SolrException.ErrorCode;
47 import org.apache.solr.common.params.CommonParams;
48 import org.apache.solr.common.params.FacetParams;
49 import org.apache.solr.common.params.GroupParams;
50 import org.apache.solr.common.params.RequiredSolrParams;
51 import org.apache.solr.common.params.SolrParams;
52 import org.apache.solr.common.util.ExecutorUtil;
53 import org.apache.solr.common.util.NamedList;
54 import org.apache.solr.common.util.SimpleOrderedMap;
55 import org.apache.solr.common.util.StrUtils;
56 import org.apache.solr.handler.component.FacetComponent;
57 import org.apache.solr.handler.component.ResponseBuilder;
58 import org.apache.solr.handler.component.SpatialHeatmapFacets;
59 import org.apache.solr.request.IntervalFacets.FacetInterval;
60 import org.apache.solr.schema.BoolField;
61 import org.apache.solr.schema.FieldType;
62 import org.apache.solr.schema.IndexSchema;
63 import org.apache.solr.schema.SchemaField;
64 import org.apache.solr.schema.TrieField;
65 import org.apache.solr.search.BitDocSet;
66 import org.apache.solr.search.DocSet;
67 import org.apache.solr.search.Grouping;
68 import org.apache.solr.search.HashDocSet;
69 import org.apache.solr.search.Insanity;
70 import org.apache.solr.search.QParser;
71 import org.apache.solr.search.QueryParsing;
72 import org.apache.solr.search.SolrIndexSearcher;
73 import org.apache.solr.search.SortedIntDocSet;
74 import org.apache.solr.search.SyntaxError;
75 import org.apache.solr.search.grouping.GroupingSpecification;
76 import org.apache.solr.util.BoundedTreeSet;
77 import org.apache.solr.util.DefaultSolrThreadFactory;
78
79 import java.io.IOException;
80 import java.util.ArrayList;
81 import java.util.Collection;
82 import java.util.Collections;
83 import java.util.IdentityHashMap;
84 import java.util.List;
85 import java.util.Map;
86 import java.util.concurrent.Callable;
87 import java.util.concurrent.ExecutionException;
88 import java.util.concurrent.Executor;
89 import java.util.concurrent.Future;
90 import java.util.concurrent.FutureTask;
91 import java.util.concurrent.RunnableFuture;
92 import java.util.concurrent.Semaphore;
93 import java.util.concurrent.SynchronousQueue;
94 import java.util.concurrent.TimeUnit;
95
96
97
98
99
100
101
102 public class SimpleFacets {
103
104
105 protected DocSet docsOrig;
106
107 protected final SolrParams global;
108
109 protected final SolrIndexSearcher searcher;
110 protected final SolrQueryRequest req;
111 protected final ResponseBuilder rb;
112
113
114 protected final static class ParsedParams {
115 final public SolrParams localParams;
116 final public SolrParams params;
117 final public SolrParams required;
118 final public String facetValue;
119 final public DocSet docs;
120 final public String key;
121 final public List<String> tags;
122 final public int threads;
123
124 public ParsedParams(final SolrParams localParams,
125 final SolrParams params,
126 final SolrParams required,
127 final String facetValue,
128 final DocSet docs,
129 final String key,
130 final List<String> tags,
131 final int threads) {
132 this.localParams = localParams;
133 this.params = params;
134 this.required = required;
135 this.facetValue = facetValue;
136 this.docs = docs;
137 this.key = key;
138 this.tags = tags;
139 this.threads = threads;
140 }
141
142 public ParsedParams withDocs(final DocSet docs) {
143 return new ParsedParams(localParams, params, required, facetValue, docs, key, tags, threads);
144 }
145 }
146
147 public SimpleFacets(SolrQueryRequest req,
148 DocSet docs,
149 SolrParams params) {
150 this(req,docs,params,null);
151 }
152
153 public SimpleFacets(SolrQueryRequest req,
154 DocSet docs,
155 SolrParams params,
156 ResponseBuilder rb) {
157 this.req = req;
158 this.searcher = req.getSearcher();
159 this.docsOrig = docs;
160 this.global = params;
161 this.rb = rb;
162 }
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177 public static boolean contains(String ref, String substring, boolean ignoreCase) {
178 if (ignoreCase)
179 return StringUtils.containsIgnoreCase(ref, substring);
180 return StringUtils.contains(ref, substring);
181 }
182
183
184 protected ParsedParams parseParams(String type, String param) throws SyntaxError, IOException {
185 SolrParams localParams = QueryParsing.getLocalParams(param, req.getParams());
186 DocSet docs = docsOrig;
187 String facetValue = param;
188 String key = param;
189 List<String> tags = Collections.emptyList();
190 int threads = -1;
191
192 if (localParams == null) {
193 SolrParams params = global;
194 SolrParams required = new RequiredSolrParams(params);
195 return new ParsedParams(localParams, params, required, facetValue, docs, key, tags, threads);
196 }
197
198 SolrParams params = SolrParams.wrapDefaults(localParams, global);
199 SolrParams required = new RequiredSolrParams(params);
200
201
202 if (type != FacetParams.FACET_QUERY) {
203 facetValue = localParams.get(CommonParams.VALUE);
204 }
205
206
207 key = facetValue;
208
209
210 key = localParams.get(CommonParams.OUTPUT_KEY, key);
211
212 String tagStr = localParams.get(CommonParams.TAG);
213 tags = tagStr == null ? Collections.<String>emptyList() : StrUtils.splitSmart(tagStr,',');
214
215 String threadStr = localParams.get(CommonParams.THREADS);
216 if (threadStr != null) {
217 threads = Integer.parseInt(threadStr);
218 }
219
220
221 String excludeStr = localParams.get(CommonParams.EXCLUDE);
222 if (excludeStr == null) return new ParsedParams(localParams, params, required, facetValue, docs, key, tags, threads);
223
224 List<String> excludeTagList = StrUtils.splitSmart(excludeStr,',');
225 docs = computeDocSet(docs, excludeTagList);
226 return new ParsedParams(localParams, params, required, facetValue, docs, key, tags, threads);
227 }
228
229 protected DocSet computeDocSet(DocSet baseDocSet, List<String> excludeTagList) throws SyntaxError, IOException {
230 Map<?,?> tagMap = (Map<?,?>)req.getContext().get("tags");
231
232 if (tagMap == null || rb == null) {
233 return baseDocSet;
234 }
235
236 IdentityHashMap<Query,Boolean> excludeSet = new IdentityHashMap<>();
237 for (String excludeTag : excludeTagList) {
238 Object olst = tagMap.get(excludeTag);
239
240 if (!(olst instanceof Collection)) continue;
241 for (Object o : (Collection<?>)olst) {
242 if (!(o instanceof QParser)) continue;
243 QParser qp = (QParser)o;
244 excludeSet.put(qp.getQuery(), Boolean.TRUE);
245 }
246 }
247 if (excludeSet.size() == 0) return baseDocSet;
248
249 List<Query> qlist = new ArrayList<>();
250
251
252 if (!excludeSet.containsKey(rb.getQuery())) {
253 qlist.add(rb.getQuery());
254 }
255
256
257 if (rb.getFilters() != null) {
258 for (Query q : rb.getFilters()) {
259 if (!excludeSet.containsKey(q)) {
260 qlist.add(q);
261 }
262 }
263 }
264
265
266 DocSet base = searcher.getDocSet(qlist);
267 if (rb.grouping() && rb.getGroupingSpec().isTruncateGroups()) {
268 Grouping grouping = new Grouping(searcher, null, rb.getQueryCommand(), false, 0, false);
269 grouping.setWithinGroupSort(rb.getGroupingSpec().getSortWithinGroup());
270 if (rb.getGroupingSpec().getFields().length > 0) {
271 grouping.addFieldCommand(rb.getGroupingSpec().getFields()[0], req);
272 } else if (rb.getGroupingSpec().getFunctions().length > 0) {
273 grouping.addFunctionCommand(rb.getGroupingSpec().getFunctions()[0], req);
274 } else {
275 return base;
276 }
277 AbstractAllGroupHeadsCollector allGroupHeadsCollector = grouping.getCommands().get(0).createAllGroupCollector();
278 searcher.search(new FilteredQuery(new MatchAllDocsQuery(), base.getTopFilter()), allGroupHeadsCollector);
279 return new BitDocSet(allGroupHeadsCollector.retrieveGroupHeads(searcher.maxDoc()));
280 } else {
281 return base;
282 }
283 }
284
285
286
287
288
289
290
291
292 @Deprecated
293 public NamedList<Object> getFacetCounts() {
294 return FacetComponent.getFacetCounts(this);
295 }
296
297
298
299
300
301
302
303 public NamedList<Integer> getFacetQueryCounts() throws IOException,SyntaxError {
304
305 NamedList<Integer> res = new SimpleOrderedMap<>();
306
307
308
309
310
311
312
313
314 String[] facetQs = global.getParams(FacetParams.FACET_QUERY);
315
316 if (null != facetQs && 0 != facetQs.length) {
317 for (String q : facetQs) {
318 final ParsedParams parsed = parseParams(FacetParams.FACET_QUERY, q);
319 getFacetQueryCount(parsed, res);
320 }
321 }
322
323 return res;
324 }
325
326 public void getFacetQueryCount(ParsedParams parsed, NamedList<Integer> res) throws SyntaxError, IOException {
327
328
329 Query qobj = QParser.getParser(parsed.facetValue, null, req).getQuery();
330
331 if (qobj == null) {
332 res.add(parsed.key, 0);
333 } else if (parsed.params.getBool(GroupParams.GROUP_FACET, false)) {
334 res.add(parsed.key, getGroupedFacetQueryCount(qobj, parsed.docs));
335 } else {
336 res.add(parsed.key, searcher.numDocs(qobj, parsed.docs));
337 }
338 }
339
340
341
342
343
344
345 public int getGroupedFacetQueryCount(Query facetQuery, DocSet docSet) throws IOException {
346
347 String groupField = global.get(GroupParams.GROUP_FIELD);
348 if (groupField == null) {
349 throw new SolrException (
350 SolrException.ErrorCode.BAD_REQUEST,
351 "Specify the group.field as parameter or local parameter"
352 );
353 }
354
355 TermAllGroupsCollector collector = new TermAllGroupsCollector(groupField);
356 Filter mainQueryFilter = docSet.getTopFilter();
357 searcher.search(new FilteredQuery(facetQuery, mainQueryFilter), collector);
358 return collector.getGroupCount();
359 }
360
361 enum FacetMethod {
362 ENUM, FC, FCS;
363 }
364
365
366
367
368
369 public NamedList<Integer> getTermCountsForPivots(String field, ParsedParams parsed) throws IOException {
370 Integer mincount = parsed.params.getFieldInt(field, FacetParams.FACET_PIVOT_MINCOUNT, 1);
371 return getTermCounts(field, mincount, parsed);
372 }
373
374
375
376
377
378
379 public NamedList<Integer> getTermCounts(String field, ParsedParams parsed) throws IOException {
380 Integer mincount = parsed.params.getFieldInt(field, FacetParams.FACET_MINCOUNT);
381 return getTermCounts(field, mincount, parsed);
382 }
383
384
385
386
387
388
389
390
391 private NamedList<Integer> getTermCounts(String field, Integer mincount, ParsedParams parsed) throws IOException {
392 final SolrParams params = parsed.params;
393 final DocSet docs = parsed.docs;
394 final int threads = parsed.threads;
395 int offset = params.getFieldInt(field, FacetParams.FACET_OFFSET, 0);
396 int limit = params.getFieldInt(field, FacetParams.FACET_LIMIT, 100);
397 if (limit == 0) return new NamedList<>();
398 if (mincount==null) {
399 Boolean zeros = params.getFieldBool(field, FacetParams.FACET_ZEROS);
400
401 mincount = (zeros!=null && !zeros) ? 1 : 0;
402
403 }
404 boolean missing = params.getFieldBool(field, FacetParams.FACET_MISSING, false);
405
406 String sort = params.getFieldParam(field, FacetParams.FACET_SORT, limit>0 ? FacetParams.FACET_SORT_COUNT : FacetParams.FACET_SORT_INDEX);
407 String prefix = params.getFieldParam(field, FacetParams.FACET_PREFIX);
408 String contains = params.getFieldParam(field, FacetParams.FACET_CONTAINS);
409 boolean ignoreCase = params.getFieldBool(field, FacetParams.FACET_CONTAINS_IGNORE_CASE, false);
410
411 NamedList<Integer> counts;
412 SchemaField sf = searcher.getSchema().getField(field);
413 FieldType ft = sf.getType();
414
415
416 final String methodStr = params.getFieldParam(field, FacetParams.FACET_METHOD);
417 FacetMethod method = null;
418 if (FacetParams.FACET_METHOD_enum.equals(methodStr)) {
419 method = FacetMethod.ENUM;
420 } else if (FacetParams.FACET_METHOD_fcs.equals(methodStr)) {
421 method = FacetMethod.FCS;
422 } else if (FacetParams.FACET_METHOD_fc.equals(methodStr)) {
423 method = FacetMethod.FC;
424 }
425
426 if (method == FacetMethod.ENUM && TrieField.getMainValuePrefix(ft) != null) {
427
428 method = sf.multiValued() ? FacetMethod.FC : FacetMethod.FCS;
429 }
430
431 if (method == null && ft instanceof BoolField) {
432
433 method = FacetMethod.ENUM;
434 }
435
436 final boolean multiToken = sf.multiValued() || ft.multiValuedFieldCache();
437
438 if (ft.getNumericType() != null && !sf.multiValued()) {
439
440
441
442 method = FacetMethod.FCS;
443 }
444
445 if (method == null) {
446
447 method = FacetMethod.FC;
448 }
449
450 if (method == FacetMethod.FCS && multiToken) {
451
452 method = FacetMethod.FC;
453 }
454
455 if (method == FacetMethod.ENUM && sf.hasDocValues()) {
456
457 method = FacetMethod.FC;
458 }
459
460 if (params.getFieldBool(field, GroupParams.GROUP_FACET, false)) {
461 counts = getGroupedCounts(searcher, docs, field, multiToken, offset,limit, mincount, missing, sort, prefix, contains, ignoreCase);
462 } else {
463 assert method != null;
464 switch (method) {
465 case ENUM:
466 assert TrieField.getMainValuePrefix(ft) == null;
467 counts = getFacetTermEnumCounts(searcher, docs, field, offset, limit, mincount,missing,sort,prefix, contains, ignoreCase, params);
468 break;
469 case FCS:
470 assert !multiToken;
471 if (ft.getNumericType() != null && !sf.multiValued()) {
472
473 if (prefix != null && !prefix.isEmpty()) {
474 throw new SolrException(ErrorCode.BAD_REQUEST, FacetParams.FACET_PREFIX + " is not supported on numeric types");
475 }
476 if (contains != null && !contains.isEmpty()) {
477 throw new SolrException(ErrorCode.BAD_REQUEST, FacetParams.FACET_CONTAINS + " is not supported on numeric types");
478 }
479 counts = NumericFacets.getCounts(searcher, docs, field, offset, limit, mincount, missing, sort);
480 } else {
481 PerSegmentSingleValuedFaceting ps = new PerSegmentSingleValuedFaceting(searcher, docs, field, offset, limit, mincount, missing, sort, prefix, contains, ignoreCase);
482 Executor executor = threads == 0 ? directExecutor : facetExecutor;
483 ps.setNumThreads(threads);
484 counts = ps.getFacetCounts(executor);
485 }
486 break;
487 case FC:
488 counts = DocValuesFacets.getCounts(searcher, docs, field, offset,limit, mincount, missing, sort, prefix, contains, ignoreCase);
489 break;
490 default:
491 throw new AssertionError();
492 }
493 }
494
495 return counts;
496 }
497
498 public NamedList<Integer> getGroupedCounts(SolrIndexSearcher searcher,
499 DocSet base,
500 String field,
501 boolean multiToken,
502 int offset,
503 int limit,
504 int mincount,
505 boolean missing,
506 String sort,
507 String prefix,
508 String contains,
509 boolean ignoreCase) throws IOException {
510 GroupingSpecification groupingSpecification = rb.getGroupingSpec();
511 final String groupField = groupingSpecification != null ? groupingSpecification.getFields()[0] : null;
512 if (groupField == null) {
513 throw new SolrException (
514 SolrException.ErrorCode.BAD_REQUEST,
515 "Specify the group.field as parameter or local parameter"
516 );
517 }
518
519 BytesRef prefixBytesRef = prefix != null ? new BytesRef(prefix) : null;
520 final TermGroupFacetCollector collector = TermGroupFacetCollector.createTermGroupFacetCollector(groupField, field, multiToken, prefixBytesRef, 128);
521
522 SchemaField sf = searcher.getSchema().getFieldOrNull(groupField);
523
524 if (sf != null && sf.hasDocValues() == false && sf.multiValued() == false && sf.getType().getNumericType() != null) {
525
526
527 searcher.search(base.getTopFilter(), new FilterCollector(collector) {
528 @Override
529 public LeafCollector getLeafCollector(LeafReaderContext context) throws IOException {
530 LeafReader insane = Insanity.wrapInsanity(context.reader(), groupField);
531 return in.getLeafCollector(insane.getContext());
532 }
533 });
534 } else {
535 searcher.search(base.getTopFilter(), collector);
536 }
537
538 boolean orderByCount = sort.equals(FacetParams.FACET_SORT_COUNT) || sort.equals(FacetParams.FACET_SORT_COUNT_LEGACY);
539 TermGroupFacetCollector.GroupedFacetResult result
540 = collector.mergeSegmentResults(limit < 0 ? Integer.MAX_VALUE :
541 (offset + limit),
542 mincount, orderByCount);
543
544 CharsRefBuilder charsRef = new CharsRefBuilder();
545 FieldType facetFieldType = searcher.getSchema().getFieldType(field);
546 NamedList<Integer> facetCounts = new NamedList<>();
547 List<TermGroupFacetCollector.FacetEntry> scopedEntries
548 = result.getFacetEntries(offset, limit < 0 ? Integer.MAX_VALUE : limit);
549 for (TermGroupFacetCollector.FacetEntry facetEntry : scopedEntries) {
550
551 if (contains != null && !contains(facetEntry.getValue().utf8ToString(), contains, ignoreCase)) {
552 continue;
553 }
554 facetFieldType.indexedToReadable(facetEntry.getValue(), charsRef);
555 facetCounts.add(charsRef.toString(), facetEntry.getCount());
556 }
557
558 if (missing) {
559 facetCounts.add(null, result.getTotalMissingCount());
560 }
561
562 return facetCounts;
563 }
564
565
566 static final Executor directExecutor = new Executor() {
567 @Override
568 public void execute(Runnable r) {
569 r.run();
570 }
571 };
572
573 static final Executor facetExecutor = new ExecutorUtil.MDCAwareThreadPoolExecutor(
574 0,
575 Integer.MAX_VALUE,
576 10, TimeUnit.SECONDS,
577 new SynchronousQueue<Runnable>()
578 , new DefaultSolrThreadFactory("facetExecutor")
579 );
580
581
582
583
584
585
586
587
588
589 @SuppressWarnings("unchecked")
590 public NamedList<Object> getFacetFieldCounts()
591 throws IOException, SyntaxError {
592
593 NamedList<Object> res = new SimpleOrderedMap<>();
594 String[] facetFs = global.getParams(FacetParams.FACET_FIELD);
595 if (null == facetFs) {
596 return res;
597 }
598
599
600
601
602 int maxThreads = req.getParams().getInt(FacetParams.FACET_THREADS, 0);
603 Executor executor = maxThreads == 0 ? directExecutor : facetExecutor;
604 final Semaphore semaphore = new Semaphore((maxThreads <= 0) ? Integer.MAX_VALUE : maxThreads);
605 List<Future<NamedList>> futures = new ArrayList<>(facetFs.length);
606
607 try {
608
609 for (String f : facetFs) {
610 final ParsedParams parsed = parseParams(FacetParams.FACET_FIELD, f);
611 final SolrParams localParams = parsed.localParams;
612 final String termList = localParams == null ? null : localParams.get(CommonParams.TERMS);
613 final String key = parsed.key;
614 final String facetValue = parsed.facetValue;
615 Callable<NamedList> callable = new Callable<NamedList>() {
616 @Override
617 public NamedList call() throws Exception {
618 try {
619 NamedList<Object> result = new SimpleOrderedMap<>();
620 if(termList != null) {
621 List<String> terms = StrUtils.splitSmart(termList, ",", true);
622 result.add(key, getListedTermCounts(facetValue, parsed, terms));
623 } else {
624 result.add(key, getTermCounts(facetValue, parsed));
625 }
626 return result;
627 } catch (SolrException se) {
628 throw se;
629 } catch (Exception e) {
630 throw new SolrException(ErrorCode.SERVER_ERROR,
631 "Exception during facet.field: " + facetValue, e);
632 } finally {
633 semaphore.release();
634 }
635 }
636 };
637
638 RunnableFuture<NamedList> runnableFuture = new FutureTask<>(callable);
639 semaphore.acquire();
640 executor.execute(runnableFuture);
641 futures.add(runnableFuture);
642 }
643
644
645 for (Future<NamedList> future : futures) {
646 res.addAll(future.get());
647 }
648 assert semaphore.availablePermits() >= maxThreads;
649 } catch (InterruptedException e) {
650 throw new SolrException(SolrException.ErrorCode.SERVER_ERROR,
651 "Error while processing facet fields: InterruptedException", e);
652 } catch (ExecutionException ee) {
653 Throwable e = ee.getCause();
654 if (e instanceof RuntimeException) {
655 throw (RuntimeException) e;
656 }
657 throw new SolrException(SolrException.ErrorCode.SERVER_ERROR,
658 "Error while processing facet fields: " + e.toString(), e);
659 }
660
661 return res;
662 }
663
664
665
666
667
668
669
670 protected NamedList<Integer> getListedTermCounts(String field, final ParsedParams parsed, List<String> terms) throws IOException {
671 FieldType ft = searcher.getSchema().getFieldType(field);
672 NamedList<Integer> res = new NamedList<>();
673 for (String term : terms) {
674 String internal = ft.toInternal(term);
675 int count = searcher.numDocs(new TermQuery(new Term(field, internal)), parsed.docs);
676 res.add(term, count);
677 }
678 return res;
679 }
680
681
682
683
684
685
686
687
688 public static int getFieldMissingCount(SolrIndexSearcher searcher, DocSet docs, String fieldName)
689 throws IOException {
690 SchemaField sf = searcher.getSchema().getField(fieldName);
691 DocSet hasVal = searcher.getDocSet
692 (sf.getType().getRangeQuery(null, sf, null, null, false, false));
693 return docs.andNotSize(hasVal);
694 }
695
696
697
698
699
700
701
702
703
704
705
706 public NamedList<Integer> getFacetTermEnumCounts(SolrIndexSearcher searcher, DocSet docs, String field, int offset, int limit, int mincount, boolean missing, String sort, String prefix, String contains, boolean ignoreCase, SolrParams params)
707 throws IOException {
708
709
710
711
712
713
714
715 int minDfFilterCache = global.getFieldInt(field, FacetParams.FACET_ENUM_CACHE_MINDF, 0);
716
717
718 DocSet fastForRandomSet = docs;
719 if (minDfFilterCache>0 && docs instanceof SortedIntDocSet) {
720 SortedIntDocSet sset = (SortedIntDocSet)docs;
721 fastForRandomSet = new HashDocSet(sset.getDocs(), 0, sset.size());
722 }
723
724
725 IndexSchema schema = searcher.getSchema();
726 LeafReader r = searcher.getLeafReader();
727 FieldType ft = schema.getFieldType(field);
728
729 boolean sortByCount = sort.equals("count") || sort.equals("true");
730 final int maxsize = limit>=0 ? offset+limit : Integer.MAX_VALUE-1;
731 final BoundedTreeSet<CountPair<BytesRef,Integer>> queue = sortByCount ? new BoundedTreeSet<CountPair<BytesRef,Integer>>(maxsize) : null;
732 final NamedList<Integer> res = new NamedList<>();
733
734 int min=mincount-1;
735 int off=offset;
736 int lim=limit>=0 ? limit : Integer.MAX_VALUE;
737
738 BytesRef prefixTermBytes = null;
739 if (prefix != null) {
740 String indexedPrefix = ft.toInternal(prefix);
741 prefixTermBytes = new BytesRef(indexedPrefix);
742 }
743
744 Fields fields = r.fields();
745 Terms terms = fields==null ? null : fields.terms(field);
746 TermsEnum termsEnum = null;
747 SolrIndexSearcher.DocsEnumState deState = null;
748 BytesRef term = null;
749 if (terms != null) {
750 termsEnum = terms.iterator();
751
752
753
754
755 if (prefixTermBytes != null) {
756 if (termsEnum.seekCeil(prefixTermBytes) == TermsEnum.SeekStatus.END) {
757 termsEnum = null;
758 } else {
759 term = termsEnum.term();
760 }
761 } else {
762
763 term = termsEnum.next();
764 }
765 }
766
767 PostingsEnum postingsEnum = null;
768 CharsRefBuilder charsRef = new CharsRefBuilder();
769
770 if (docs.size() >= mincount) {
771 while (term != null) {
772
773 if (prefixTermBytes != null && !StringHelper.startsWith(term, prefixTermBytes))
774 break;
775
776 if (contains == null || contains(term.utf8ToString(), contains, ignoreCase)) {
777 int df = termsEnum.docFreq();
778
779
780
781
782 if (df > 0 && df > min) {
783 int c;
784
785 if (df >= minDfFilterCache) {
786
787
788 if (deState == null) {
789 deState = new SolrIndexSearcher.DocsEnumState();
790 deState.fieldName = field;
791 deState.liveDocs = r.getLiveDocs();
792 deState.termsEnum = termsEnum;
793 deState.postingsEnum = postingsEnum;
794 }
795
796 c = searcher.numDocs(docs, deState);
797
798 postingsEnum = deState.postingsEnum;
799 } else {
800
801
802
803
804
805 postingsEnum = termsEnum.postings(postingsEnum, PostingsEnum.NONE);
806 c = 0;
807
808 if (postingsEnum instanceof MultiPostingsEnum) {
809 MultiPostingsEnum.EnumWithSlice[] subs = ((MultiPostingsEnum) postingsEnum).getSubs();
810 int numSubs = ((MultiPostingsEnum) postingsEnum).getNumSubs();
811 for (int subindex = 0; subindex < numSubs; subindex++) {
812 MultiPostingsEnum.EnumWithSlice sub = subs[subindex];
813 if (sub.postingsEnum == null) continue;
814 int base = sub.slice.start;
815 int docid;
816 while ((docid = sub.postingsEnum.nextDoc()) != DocIdSetIterator.NO_MORE_DOCS) {
817 if (fastForRandomSet.exists(docid + base)) c++;
818 }
819 }
820 } else {
821 int docid;
822 while ((docid = postingsEnum.nextDoc()) != DocIdSetIterator.NO_MORE_DOCS) {
823 if (fastForRandomSet.exists(docid)) c++;
824 }
825 }
826
827 }
828
829 if (sortByCount) {
830 if (c > min) {
831 BytesRef termCopy = BytesRef.deepCopyOf(term);
832 queue.add(new CountPair<>(termCopy, c));
833 if (queue.size() >= maxsize) min = queue.last().val;
834 }
835 } else {
836 if (c >= mincount && --off < 0) {
837 if (--lim < 0) break;
838 ft.indexedToReadable(term, charsRef);
839 res.add(charsRef.toString(), c);
840 }
841 }
842 }
843 }
844 term = termsEnum.next();
845 }
846 }
847
848 if (sortByCount) {
849 for (CountPair<BytesRef,Integer> p : queue) {
850 if (--off>=0) continue;
851 if (--lim<0) break;
852 ft.indexedToReadable(p.key, charsRef);
853 res.add(charsRef.toString(), p.val);
854 }
855 }
856
857 if (missing) {
858 res.add(null, getFieldMissingCount(searcher,docs,field));
859 }
860
861 return res;
862 }
863
864
865
866
867
868
869 public static class CountPair<K extends Comparable<? super K>, V extends Comparable<? super V>>
870 implements Comparable<CountPair<K,V>> {
871
872 public CountPair(K k, V v) {
873 key = k; val = v;
874 }
875 public K key;
876 public V val;
877 @Override
878 public int hashCode() {
879 return key.hashCode() ^ val.hashCode();
880 }
881 @Override
882 public boolean equals(Object o) {
883 if (! (o instanceof CountPair)) return false;
884 CountPair<?,?> that = (CountPair<?,?>) o;
885 return (this.key.equals(that.key) && this.val.equals(that.val));
886 }
887 @Override
888 public int compareTo(CountPair<K,V> o) {
889 int vc = o.val.compareTo(val);
890 return (0 != vc ? vc : key.compareTo(o.key));
891 }
892 }
893
894
895
896
897
898
899
900
901
902
903 public NamedList<Object> getFacetIntervalCounts() throws IOException, SyntaxError {
904 NamedList<Object> res = new SimpleOrderedMap<Object>();
905 String[] fields = global.getParams(FacetParams.FACET_INTERVAL);
906 if (fields == null || fields.length == 0) return res;
907
908 for (String field : fields) {
909 final ParsedParams parsed = parseParams(FacetParams.FACET_INTERVAL, field);
910 String[] intervalStrs = parsed.required.getFieldParams(parsed.facetValue, FacetParams.FACET_INTERVAL_SET);
911 SchemaField schemaField = searcher.getCore().getLatestSchema().getField(parsed.facetValue);
912 if (parsed.params.getBool(GroupParams.GROUP_FACET, false)) {
913 throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "Interval Faceting can't be used with " + GroupParams.GROUP_FACET);
914 }
915
916 SimpleOrderedMap<Integer> fieldResults = new SimpleOrderedMap<Integer>();
917 res.add(parsed.key, fieldResults);
918 IntervalFacets intervalFacets = new IntervalFacets(schemaField, searcher, parsed.docs, intervalStrs, parsed.params);
919 for (FacetInterval interval : intervalFacets) {
920 fieldResults.add(interval.getKey(), interval.getCount());
921 }
922 }
923
924 return res;
925 }
926
927 public NamedList getHeatmapCounts() throws IOException, SyntaxError {
928 final NamedList<Object> resOuter = new SimpleOrderedMap<>();
929 String[] unparsedFields = rb.req.getParams().getParams(FacetParams.FACET_HEATMAP);
930 if (unparsedFields == null || unparsedFields.length == 0) {
931 return resOuter;
932 }
933 if (global.getBool(GroupParams.GROUP_FACET, false)) {
934 throw new SolrException(SolrException.ErrorCode.BAD_REQUEST,
935 "Heatmaps can't be used with " + GroupParams.GROUP_FACET);
936 }
937 for (String unparsedField : unparsedFields) {
938 final ParsedParams parsed = parseParams(FacetParams.FACET_HEATMAP, unparsedField);
939
940 resOuter.add(parsed.key, SpatialHeatmapFacets.getHeatmapForField(parsed.key, parsed.facetValue, rb, parsed.params, parsed.docs));
941 }
942 return resOuter;
943 }
944
945 public SolrParams getGlobalParams() {
946 return global;
947 }
948
949 public DocSet getDocsOrig() {
950 return docsOrig;
951 }
952
953 public SolrQueryRequest getRequest() {
954 return req;
955 }
956
957 public ResponseBuilder getResponseBuilder() {
958 return rb;
959 }
960 }
961